package org.gpp.proj1.logic.core.client.init;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

import org.gpp.proj1.communication.listener.RequestListener;
import org.gpp.proj1.communication.manager.NetworkManager;
import org.gpp.proj1.communication.protocol.Message;
import org.gpp.proj1.communication.protocol.ProtocolIdentifiers;
import org.gpp.proj1.communication.protocol.ProtocolMessageFactory;
import org.gpp.proj1.communication.protocol.ProtocolMessageParser;
import org.gpp.proj1.communication.protocol.ProtocolParameters;
import org.gpp.proj1.logic.core.board.RobotPosition;
import org.gpp.proj1.logic.metadata.RobotPartCategory;
import org.gpp.proj1.map.GameMap;
import org.gpp.proj1.map.MalformedMapFileException;
import org.gpp.proj1.map.parser.MapFileParser;
import org.gpp.proj1.ui.controller.ISRGUIController;

public class GameInitThread implements Runnable, RequestListener {
	
	private final ISRGUIController controller;
	private final NetworkManager networkManager;	
	private final String serverId;
	private final String clientId;
	private final Map<String, String> aliasMap;
	
	private boolean isRunning;
	
	private String alias;
	private GameMap gameMap;
	
	private BlockingQueue<Message> receivedMessages;
	private Map<String, Map<String, Map<RobotPartCategory, String>>> clientRobotMapping;
	private Map<String, Map<String, RobotPosition>> robotPositions;
	

	public GameInitThread( String clientId, String serverId, String alias, Map<String, String> aliasMap, ISRGUIController controller, NetworkManager networkManager ) {
		this.clientId = clientId;
		this.serverId = serverId;
		this.alias = alias;
		this.aliasMap = aliasMap;
		this.controller = controller;
		this.networkManager = networkManager;
		
		this.clientRobotMapping = new HashMap<String, Map<String,Map<RobotPartCategory,String>>>();
		
		this.receivedMessages = new ArrayBlockingQueue<Message>( 100 );
		this.isRunning = false;
		
		this.networkManager.registerRequestListener( this );
	}

	public void run() {

		this.isRunning = true;
		
		this.controller.notifyInitPhaseStarted();
		
		// Sending the robot list message
		this.controller.updateInitStatus( "Sending robot data.." );
		Message robotsMessage = ProtocolMessageFactory.buildMyRobotListMessage( this.alias, this.controller.getPlayerRobots() );
		this.networkManager.sendMessage( serverId, robotsMessage );
		this.controller.updateInitStatus( "Robot data sent to server." );
		
		while( this.isRunning ) {
			try {
				Message message = this.receivedMessages.take();
				
				if( message.getIdentifier().equals( ProtocolIdentifiers.MAPDATA ) ) {
					
					this.controller.updateInitStatus( "Parsing map data..." );
					try {
						this.gameMap = MapFileParser.parseFileFromString( message.getParameters()[ ProtocolParameters.MAPDATA_DATA ] );
					} catch (IOException e) {
						this.controller.updateInitStatus( "Map parsing failed!" );
						this.controller.notifyInitPhaseFailed( "Map parsing failed. Cause: " + e.getMessage() );
					} catch (MalformedMapFileException e) {
						this.controller.updateInitStatus( "Map parsing failed!" );
						this.controller.notifyInitPhaseFailed( "Map parsing failed. Cause: " + e.getMessage() );
					}
					this.controller.updateInitStatus( "Parsed map data." );
				} else if( message.getIdentifier().equals( ProtocolIdentifiers.PLAYEROBOTDATA ) ) {

					String playerId = message.getParameters()[ ProtocolParameters.PLAYERROBOTDATA_ID ];
					
					this.controller.updateInitStatus( "Parsing robot data for player \"" + aliasMap.get( playerId ) + "\"..." );
					
					Map<String, Map<RobotPartCategory, String>> robotMapping = ProtocolMessageParser.parseRobotData( message );
					
					this.clientRobotMapping.put( playerId, robotMapping );
					
					this.controller.updateInitStatus( "Parsed player robot data." );
				} else if( message.getIdentifier().equals( ProtocolIdentifiers.GAMEBOARDDATA ) ) {

					this.controller.updateInitStatus( "Parsing game board initial positions..." );
					
					this.robotPositions = ProtocolMessageParser.parseGameBoard( message );
					
					this.controller.updateInitStatus( "Parsed initial positions." );
					
					Message readyMessage = ProtocolMessageFactory.buildToggleReadinessMessage();
					this.networkManager.sendMessage( serverId, readyMessage );
				} else if( message.getIdentifier().equals( ProtocolIdentifiers.INGAMEPHASE ) ) {

					this.isRunning = false;
					this.networkManager.unregisterRequestListener( this );
					
					this.controller.updateInitStatus( "Starting game!" );
					
					this.controller.gameHasStarted( this.gameMap, this.robotPositions, this.clientRobotMapping );
				} else if( message.getIdentifier().equals( ProtocolIdentifiers.INITFAILED ) ) {
					// TODO notify interface. Note that if the server goes down, the client
					// will be notified first, so it must end the init thread!!
				}
			} catch (InterruptedException e) {
			}
		}
	}

	public void requestReceived(Message message) {
		
		this.receivedMessages.offer( message );
	}

}
